#ifndef __select__SVR_H__
#define __select__SVR_H__

#include <iostream>
#include <string>
#include <sys/select.h>
#include <sys/time.h>
#include "log.hpp"
#include "Sock.hpp"

// #define BITS 8
// #define NUM (sizeof(fd_set) * BITS)
// #define FD_NONE -1

using namespace std;

// select 我们只完成读取，写入和异常不做处理 -- epoll(写完整)
class SelectServer
{
public:
    SelectServer(const uint16_t& port = 8080)
        : _port(port)
    {
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);
        logMessage(DEBUG, "%s", "create base socket success");
        // for (int i = 0; i < NUM; i++)
        // {
        //     _fd_array[i] = FD_NONE;
        // }
    }

    void Start()
    {
        fd_set rfds;
        FD_ZERO(&rfds); // 先清空
        while (true)
        {
            struct timeval timeout = {5, 0};
            // 如何看待listensock? 获取新连接，我们把他依旧看成IO，input事件，如果没有连接到来呢？阻塞
            // int sock = Sock::Accept(listensock, ...); // 不能直接调用accept了
            FD_SET(_listensock, &rfds); // 将listensock添加到读文件的文件描述符集中
            // int n = select(_listensock + 1, &rfds, nullptr, nullptr, &timeout);

            // 1.nfds:随着我们获取的sock越来越多，随着我们添加到select的sock越来越多，注定了nfds每一次都可能要变化，我们需要对他进行动态计算
            // 2.rfds/writefds/exceptfds: 都是输入输出型参数，输入输出不一定是一样的，所以注定了我们每一次都要对rfds进行重新添加
            // 3.timeout: 都是输入输出型参数，每一次都要进行重置，前提是你有需要的话
            // 1,2 => 注定了我们必须自己将合法的文件描述符需要单独保存起来，用来支持：1.更新最大fd 2.更新位图结构
            int n = select(_listensock + 1, &rfds, nullptr, nullptr, nullptr);
            switch (n)
            {
            case 0:
                //printf("hello select ...\n");
                logMessage(DEBUG, "%s", "time out");
                break;
            case -1:
                logMessage(WARNING, "select error: %d : %s", errno, strerror(errno));
                break;
            default:
                // 成功的
                logMessage(DEBUG, "get a new link ..."); // 为什么会一直打印连接的到来呢？连接已经建立完成，就绪了，但是你没有取走，select要一直通知你！
                HandlerEvent(rfds);
                break;
            }
        }
    }

    ~SelectServer()
    {
        if (_listensock >= 0)
            close(_listensock);
    }

private:
    void HandlerEvent(const fd_set& rfds)  // fd_set 是一个集合，里面可能存在多个sock
    {
        string clientip;
        uint16_t clientport = 0;
        if (FD_ISSET(_listensock, &rfds))
        {
            // listensock上面的读事件就绪了，表示可以读取了
            // 获取连接了
            int sock = Sock::Accept(_listensock, &clientip, &clientport); // 这里再进行accept会不会阻塞？ 不会
            if(sock < 0)
            {
                logMessage(WARNING, "accept error");
                return ;
            }
            logMessage(DEBUG, "get a new line success : [%s:%d] : %d", clientip.c_str(), clientport, sock);

            // TODO
            // read / recv?  不能！ 为什么不能？ 我们不清楚sock上面数据什么时候到来， recv、read就有可能先被阻塞， IO = 等 + 数据拷贝
            // 谁清楚数据就绪呢？ select！
            // 得到新连接的时候，此时我们应该考虑的是，将新的sock托管给select，让select帮我们进行检测sock上是否有新的数据
            // 有了数据select，读事件就绪，select就会通知我，我们在进行读取，此时就不会再被阻塞了。
        }
    }

private:
    uint16_t _port;
    int _listensock;
    //int _fd_array[NUM];
};

#endif
